/**
 * 后端 LLM Provider
 * 适配项目：tp8-server-scaffold <https://gitee.com/darkwinoom/tp8-server-scaffold>
 */
import type { LLMCompletionsData, LLMCompletionsCb, LLMModel } from '@/provider/llm/type'
import type { RequestMethods } from '@/utils/http/types'
import { llmBaseProvider } from '@/provider/llm/baseProvider'
import { ref } from 'vue'
import { XStream } from 'ant-design-x-vue'
import { geneTitlePrompt, clearContent } from '@/provider/llm/hooks/utils'
import { http } from '@/utils/http'
import { joinUrls } from '@/utils/generate'
import { useUserStore } from '@/stores/user'

class BackendProvider extends llmBaseProvider {
  // 打断流数据
  private streamAbort = ref(() => {})

  /**
   * 内部请求
   *
   * @param method 请求类型
   * @param uri 地址
   * @param data 数据体
   * @returns
   */
  private async request(method: RequestMethods, uri: string, data?: object, quite?: boolean) {
    return await http.request<HttpResponse>(
      method,
      joinUrls(this.getBaseUrl(), uri),
      {
        data,
      },
      { quite },
    )
  }

  protected init() {
    // watchEffect(() => {
    //   onWatcherCleanup(() => {
    //     this.streamAbort.value()
    //   })
    // })
  }

  public async check() {
    // 尝试调用 api 以判断是否可用
    try {
      await this.request('get', 'models', {}, true)
      return true
    } catch {
      return false
    }
  }

  public async getModels() {
    const response: LLMModel[] = []
    const models = await this.request('get', 'models')
    for (const item of models.data) {
      response.push({
        key: item.modelKey,
        type: 'llm',
        name: item.displayName,
      })
    }
    return response
  }

  public async geneTitle(model: string, messages: LLMMessage[]) {
    try {
      const result = await this.request('post', 'chat/generate', {
        model,
        prompt: geneTitlePrompt(messages),
        temperature: 0.3,
        max_tokens: 40,
      })

      return result.data.content
        .replace(/<[^>]*>/g, '')
        .replace(/\n/g, '')
        .trim()
    } catch (error) {
      window.console.error(error)
      return ''
    }
  }

  public async completions(data: LLMCompletionsData, callback: LLMCompletionsCb) {
    try {
      let content = ''
      let isError = false
      const messages = []
      for (const item of data.messages) {
        if (item.role !== 'error') {
          messages.push({
            role: item.role,
            content: clearContent(item.content),
            // 可自行处理图片上传逻辑
            // 通常情况下，推荐将图片上传至服务器，并在请求时提交ID，可减少请求参数体积
            // 这部分功能目前暂未实装，会在后续扩展
            images: [],
          })
        }
      }
      const response = await window.fetch(joinUrls(this.getBaseUrl(), 'chat/completions'), {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Authorization: 'Bearar ' + useUserStore().getAccessToken(),
        },
        body: JSON.stringify({
          model: data.model,
          messages,
          temperature: data.temperature || undefined,
          maxTokens: data.max_tokens || false,
          stream: true,
        }),
      })

      if (!response.ok) {
        throw new Error(response.statusText)
      }

      const stream = XStream({
        readableStream: response.body,
      })

      const reader = stream.getReader()
      this.streamAbort.value = () => {
        reader?.cancel()
      }

      while (reader) {
        const { value, done } = await reader.read()
        if (done) {
          if (isError) {
            callback.onError(new Error(content))
          } else {
            callback.onSuccess(content)
          }
          break
        }
        if (!value) continue
        try {
          const res = JSON.parse(value.data)
          if (res.error) {
            // 标记错误（这里需要后端配合）
            isError = true
          }
          content += res.choices[0].delta.content || ''
          callback.onUpdate(content)
        } catch {
          //
        }
      }
    } catch (error) {
      callback.onError(error)
    }
  }

  public async abort() {
    this.streamAbort.value()
  }
}

export default BackendProvider
